﻿using System;
using System.Collections.Generic;
using System.Text;

namespace TAC
{
	class NonPlayerCharacter : Character
	{
		/*--------------------------------------------------------------------------------------------------------*/
		/*:::[FIELDS]:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
		/*--------------------------------------------------------------------------------------------------------*/

		//model3d
		protected int[] attribute;
		private bool[] status;
		private Character opponent;
		private Dice dice;
		private int X;
		private int Y;

		/*--------------------------------------------------------------------------------------------------------*/
		/*:::[CONSTRUCTORS]:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
		/*--------------------------------------------------------------------------------------------------------*/

		public NonPlayerCharacter()
		{
			attribute = new int[ID.NUMBER_OF_ATTRIBUTES];
			status = new bool[ID.NUMBER_OF_STATUS];
			dice = new Dice();
		}

        public NonPlayerCharacter(ref int[] clone)
        {
            attribute = new int[ID.NUMBER_OF_ATTRIBUTES];
            status = new bool[ID.NUMBER_OF_STATUS];
            dice = new Dice();

            for (int i = 0; i < clone.Length; i++)
                attribute[i] = clone[i];

            recalc();
        }

        public NonPlayerCharacter(ref NonPlayerCharacter clone)
		{
			attribute = clone.attribute;
			status = clone.status;
			dice = clone.dice;
		}

		/*--------------------------------------------------------------------------------------------------------*/
		/*:::[GETTERS]::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
		/*--------------------------------------------------------------------------------------------------------*/

		//preconditions: strenght <= id <= total points
		public int getAttribute(int id)
		{
			return attribute[id];
		}

		public Character getAttackedBy()
		{
			return opponent;
		}

		public int getX()
		{
			return X;
		}

		public int getY()
		{
			return Y;
		}

		/*--------------------------------------------------------------------------------------------------------*/
		/*:::[SETTERS]::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
		/*--------------------------------------------------------------------------------------------------------*/

		//preconditions: strenght <= id <= intelligence || id = experience
		public void setAttribute(int id, int value)
		{
			attribute[id] = value;
			recalc();
		}

		public void setAttackedBy(Character opponent)
		{
			this.opponent = opponent;
		}

		public void setX(int i)
		{
			X = i;
		}

		public void setY(int j)
		{
			Y = j;
		}

		/*--------------------------------------------------------------------------------------------------------*/
		/*:::[ROLLS]::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
		/*--------------------------------------------------------------------------------------------------------*/

		public int rollInitiative()
		{
			return dice.roll() + attribute[ID.DEXTERITY];
		}

		//preconditions: id = dexterity || id = intelligence
		public int rollAttack(int id)
		{
			return dice.roll() + attribute[id];
		}

		public int rollDodge()
		{
			return dice.roll() + attribute[ID.SPEED];
		}

		/*--------------------------------------------------------------------------------------------------------*/
		/*:::[ACTIONS]::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
		/*--------------------------------------------------------------------------------------------------------*/

		public void BasicAttack(ref Character opponent)
		{
			int attack, damage, dodge;
			attack = rollAttack(ID.DEXTERITY);
			damage = DamageOfAttack(ID.DEXTERITY);
			dodge = opponent.rollDodge();
			if (dice.critic || attack > dodge)
			{
				opponent.takeDamage(damage);
				opponent.setAttackedBy(this);
			}
		}

		public void SpecialAttack(ref Character opponent)
		{
			int attack, damage, dodge;
			attack = rollAttack(ID.INTELLIGENCE);
			damage = DamageOfAttack(ID.INTELLIGENCE);
			dodge = opponent.rollDodge();
			if (dice.critic || attack > dodge)
				opponent.takeDamage(damage);
			takeDamage(Math.Max(1, damage/4));
		}

		public void Defense()
		{
			Defense(0);
		}

		//preconditions: xp >= attribute[experience]
		public void Heal(int xp)
		{
			attribute[ID.DAMAGE] = Math.Max(0, attribute[ID.DAMAGE] - xp/4 * attribute[ID.CONSTITUTION]);
			attribute[ID.EXPERIENCE] -= xp;
		}

		/*--------------------------------------------------------------------------------------------------------*/
		/*:::[OTHER]::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
		/*--------------------------------------------------------------------------------------------------------*/

		public void takeDamage(int damage)
		{
			Defense(damage);
			attribute[ID.DAMAGE] = Math.Min(attribute[ID.HEALTH], attribute[ID.DAMAGE] + damage);
		}

		public bool isDead()
		{
			return attribute[ID.DAMAGE] >= attribute[ID.HEALTH];
		}

		/*--------------------------------------------------------------------------------------------------------*/
		/*:::[AUXILIARY]::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
		/*--------------------------------------------------------------------------------------------------------*/

		//preconditions: id = dexterity || id = intelligence
		private int DamageOfAttack(int id)
		{
			int modifier, sides, times;
			sides = (id == ID.DEXTERITY) ? 6 : 12;
			times = attribute[id] / 4;
			modifier = attribute[id] % 4;
			return dice.roll(sides, times) + modifier;
		}

		private void Defense(int damage)
		{
			status[ID.DEFENSE] = true;
			if (damage > attribute[ID.CONSTITUTION])
				status[ID.DEFENSE] = false;
			if (status[ID.DEFENSE])
				attribute[ID.PROTECTION] = dice.roll(6, 1);
			else attribute[ID.PROTECTION] = 1;
		}

		private void recalc()
		{
			/*Calculates Hit Points*/
			attribute[ID.HEALTH] = Math.Max(1, 4 * attribute[ID.CONSTITUTION]);
			/*Calculates Protection*/
			attribute[ID.PROTECTION] = 1;
			/*Calculates Speed*/
			attribute[ID.SPEED] = (attribute[ID.DEXTERITY] + attribute[ID.CONSTITUTION]) / 4;
			/*Calculates Total Points*/
			attribute[ID.TOTAL_POINTS] = 0;
			for (int id = ID.STRENGHT; id <= ID.INTELLIGENCE; ++id)
				attribute[ID.TOTAL_POINTS] += attribute[id];
		}
	}
}
